package com.xxl.job.core.util;

import com.fish.common.core.util.ReturnT;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.*;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.cert.X509Certificate;

/**
 * @author xuxueli
 * @since 2018-11-25 00:55:31
 */
public class XxlJobRemotingUtil {

	public static final String XXL_JOB_ACCESS_TOKEN = "XXL-JOB-ACCESS-TOKEN";

	private static final Logger logger = LoggerFactory.getLogger(XxlJobRemotingUtil.class);

	private static final TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
		@Override
		public X509Certificate[] getAcceptedIssuers() {
			return new X509Certificate[] {};
		}

		@Override
		public void checkClientTrusted(X509Certificate[] chain, String authType) {
		}

		@Override
		public void checkServerTrusted(X509Certificate[] chain, String authType) {
		}
	} };

	// trust-https start
	private static void trustAllHosts(HttpsURLConnection connection) {
		try {
			SSLContext sc = SSLContext.getInstance("TLS");
			sc.init(null, trustAllCerts, new java.security.SecureRandom());
			SSLSocketFactory newFactory = sc.getSocketFactory();

			connection.setSSLSocketFactory(newFactory);
		}
		catch (Exception e) {
			logger.error(e.getMessage(), e);
		}
		connection.setHostnameVerifier((hostname, session) -> true);
	}

	// trust-https end

	/**
	 * post
	 * @param url url
	 * @param accessToken token
	 * @param timeout timeout
	 * @param requestObj req
	 * @param returnTargClassOfT return
	 * @return ReturnT
	 */
	public static ReturnT postBody(String url, String accessToken, int timeout, Object requestObj,
			Class returnTargClassOfT) {
		HttpURLConnection connection = null;
		BufferedReader bufferedReader = null;
		try {
			// connection
			URL realUrl = new URL(url);
			connection = (HttpURLConnection) realUrl.openConnection();

			// trust-https
			boolean useHttps = url.startsWith("https");
			if (useHttps) {
				HttpsURLConnection https = (HttpsURLConnection) connection;
				trustAllHosts(https);
			}

			// connection setting
			connection.setRequestMethod("POST");
			connection.setDoOutput(true);
			connection.setDoInput(true);
			connection.setUseCaches(false);
			connection.setReadTimeout(timeout * 1000);
			connection.setConnectTimeout(3 * 1000);
			connection.setRequestProperty("connection", "Keep-Alive");
			connection.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
			connection.setRequestProperty("Accept-Charset", "application/json;charset=UTF-8");

			if (accessToken != null && !accessToken.trim().isEmpty()) {
				connection.setRequestProperty(XXL_JOB_ACCESS_TOKEN, accessToken);
			}

			// do connection
			connection.connect();

			// write requestBody
			if (requestObj != null) {
				String requestBody = GsonTool.toJson(requestObj);

				DataOutputStream dataOutputStream = new DataOutputStream(connection.getOutputStream());
				dataOutputStream.write(requestBody.getBytes(StandardCharsets.UTF_8));
				dataOutputStream.flush();
				dataOutputStream.close();
			}

			/*
			 * byte[] requestBodyBytes = requestBody.getBytes("UTF-8");
			 * connection.setRequestProperty("Content-Length",
			 * String.valueOf(requestBodyBytes.length)); OutputStream outwritestream =
			 * connection.getOutputStream(); outwritestream.write(requestBodyBytes);
			 * outwritestream.flush(); outwritestream.close();
			 */

			// valid StatusCode
			int statusCode = connection.getResponseCode();
			if (statusCode != 200) {
				return ReturnT.instance(ReturnT.FAIL_CODE,
						"xxl-job remoting fail, StatusCode(" + statusCode + ") invalid. for url : " + url);
			}

			// result
			bufferedReader = new BufferedReader(
					new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8));
			StringBuilder result = new StringBuilder();
			String line;
			while ((line = bufferedReader.readLine()) != null) {
				result.append(line);
			}
			String resultJson = result.toString();

			// parse returnT
			try {
				ReturnT returnT = GsonTool.fromJson(resultJson, ReturnT.class, returnTargClassOfT);
				return returnT;
			}
			catch (Exception e) {
				logger.error("xxl-job remoting (url={}) response content invalid({}).", url, resultJson, e);
				return ReturnT.instance(ReturnT.FAIL_CODE,
						"xxl-job remoting (url=" + url + ") response content invalid(" + resultJson + ").");
			}

		}
		catch (Exception e) {
			logger.error(e.getMessage(), e);
			return ReturnT.instance(ReturnT.FAIL_CODE,
					"xxl-job remoting error(" + e.getMessage() + "), for url : " + url);
		}
		finally {
			try {
				if (bufferedReader != null) {
					bufferedReader.close();
				}
				if (connection != null) {
					connection.disconnect();
				}
			}
			catch (Exception e2) {
				logger.error(e2.getMessage(), e2);
			}
		}
	}

}
